﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using Accord.MachineLearning;
using Accord.Math;
using Accord.Statistics;
namespace Accord.MachineLearning
{
    public class resampling
    {
        public const int C_MAX = 10;               //c* in our algorithem. max number of cluster to check
        public const int J_MAX = 10;             //number of itertion for each c 
        int semple_size;                         //t in our algorithem
       // int[] clusterScatter;
        double[][] imagePixel;
        double[,] allIteration=new double[3,3];
        double[,] helpa, helpb, helpc, helpd, PixelMatrix, MatA, NewD1, NewD2;
        int iter;
        
       
        double[] resultArr;
        double[] resultJArr;
        int[] sampOn;

        public resampling(int size, double[][] pxl, int itera)
        {
          //  this.clusterScatter = scat;
            this.imagePixel = pxl;
            this.semple_size = size/2;      //each semple we take half amount of pixel
            this.iter = itera;
            this.sampOn = new int[imagePixel.Length];
        }

         public double [,] GetAllIteration
        {
            get { return allIteration; }
            set { allIteration = value; }
        }

         double traceMat(double[,] mat)
         {
             double trace=0;
             for (int i = 0; i < this.semple_size; i++)
                 for (int j = 0; j < this.semple_size; j++)
                 {
                     if (i == j)
                         trace =trace+ mat[i,j];
                 }
             return trace;
         }

         public double calc_trace(double[,] w1, double[,] w2)
         {
             //D_i (W_1^((j)),W_2^((j)) )= |trace((W_1^((j)) )^(-1) W_2^((j)) )+trace((W_2^((j)) )^(-1) W_1^((j)) )-2d|
           //  int d = 3;
             double[,] w1Tran=null;
             //double[,] w2Tran = null;

             w1Tran=w1.substriction(w2);
             w1Tran = w1Tran.Abs();
             //double max=w1Tran[0,0];
             double max=0;
             for (int i = 0; i < w1Tran.GetLength(0); i++)
                 for (int j = 0; j < w1Tran.GetLength(1); j++)
                     max += w1Tran[i, j];
             max /= w1Tran.Length;
                     //if (max < w1Tran[i, j])
                        // max = w1Tran[i, j];
             return max;

           /*
             if (w1.Det() == 0.0)
                 w1Tran = Accord.Math.Matrix.Identity(w1.GetLength(0));
             else w1Tran = w1.inverse();     
             double[,] w1mul = w1Tran.MultiplyMatrix(w2);
             double traceW1tran = traceMat(w1mul);
             double traceW1tran = traceMat(w1Tran);

             if (w2.Det() == 0.0)
                 w2Tran = Accord.Math.Matrix.Identity(w2.GetLength(0));
             else w2Tran = w2.inverse();     
             double[,] w2mul = w2Tran.MultiplyMatrix(w1);
             double traceW2tran = traceMat(w2mul);
             double traceW2tran = traceMat(w2Tran);

             double result = System.Math.Abs(traceW1tran + traceW2tran  - (2 * d));
             double result = System.Math.Abs(traceW1tran - traceW2tran - (2 * d));
             if (result < 0) result = 0 - result;
               return result;
             */
         }

         void sampOnZiro()
         {
             for (int i = 0; i < this.sampOn.Length; i++)
                 this.sampOn[i] = 0;
         }

        public double[,] calculateD(double[,] right, double[,] left)
        {
            double[,] d  = new double[right.GetLength(0), left.GetLength(1)];
            if (right.GetLength(1) == left.GetLength(0))
            {
                 
                for (int i = 0; i < d.GetLength(0); i++)
                {
                    for (int j = 0; j < d.GetLength(1); j++)
                    {
                       d[i, j] = 0;
                        for (int k = 0; k < right.GetLength(1); k++)
                            d[i, j] = d[i, j] + right[i, k] * left[k, j];
                    }
                }
              
            }
            return d;
        }

        public double[] improoveResult()
        {
          this.resultArr=new double[C_MAX];  //holds the reults fot each c
          for (int c = 2; c < C_MAX; c++)
          {
              Console.WriteLine("iteration C:"+(c-2));
              sampOnZiro();

           //   Console.WriteLine("after first sampon");
               get_resampl(c);

            //   Console.WriteLine("first sample");
               this.resultArr[c-2] = this.resultJArr[5] / this.resultJArr[7];      //5/7
          }

          //int res = fimdMinArray(this.resultArr);
          return resultArr;

        }

        public int fimdMinArray(double[] arr)
        {
            int min=2;
            for (int i = 2; i < arr.Length; i++)
            {
                if (arr[min] < arr[i])
                    min = i;
            }
            return min;
        }

        public double[][] getSamp(int start, int end)      //copy the semple from the original array pixel
        {
            double[][] data = new double[this.imagePixel.Length / 2][];
            if (start == 1)
            {
                for (int i = 0; i < this.imagePixel.Length / 2; i++)
                {
                    Random random = new Random();             //gets rendom pixel
                    int num = random.Next(0, this.imagePixel.Length);
                    while (this.sampOn[num] == 1)
                    {
                        random = new Random();             //gets rendom pixel
                        num = random.Next(0, this.imagePixel.Length);
                    }
                    if (this.sampOn[num] == 0)               //in case this pixel have not chosen yet
                    {
                        this.sampOn[num] = 1;
                        data[i] = new double[3];
                        for (int j = 0; j < 3; j++)
                            data[i][j] = this.imagePixel[num][j];
                    }
                }
            }
            else if (end == 1)
            {
                int k = 0;
                for (int i = 0; i < this.imagePixel.Length / 2; i++)
                {
                     while (this.sampOn[k] == 1) k++;
                    if (this.sampOn[k] == 0)
                    {
                        this.sampOn[k] = 1;
                        data[i] = new double[3];
                        for (int j = 0; j < 3; j++)
                            data[i][j] = this.imagePixel[k][j];
                    }
                }
            }
            return data;
        }


        public void get_resampl(int current_c)
        {
           
            double[][] semple1;
            this.resultJArr = new double[J_MAX];
            sampOnZiro();
          //  Console.WriteLine("first sample");
           // semple1 = getSamp(1, 0);
          //  Console.WriteLine("sec sample");
           // double[][] semple2 = getSamp(0, 1);

            for (int j = 2; j < J_MAX; j++)
            {
              //  Console.WriteLine("iteration j:" +(j-2));
                sampOnZiro();
                //--------------------------------------------------------------------  //creating first semples
            
                Console.WriteLine("first sample");
                semple1 = getSamp(1,0);
             
                // Compute the K-Means algorithm until the difference in
                //  cluster centroids between two iterations is below 0.05
               
                for (int i = 0; i < this.iter; i++)
                {
                    KMeans kmeans1 = new KMeans(current_c, Distance.SquareEuclidean);
                    int[] idx1 = kmeans1.Compute(semple1, 0.05);  //tolerance value

                    myKmeans mykmean1 = new myKmeans(current_c, idx1, semple1, 0.01);
                    double[,] a = mykmean1.setAinIterationI();
                    double[,] b = allIteration.addition(a);
                    this.GetAllIteration = b;
                }

            if (this.GetAllIteration.Det() == 0.0) 
                MatA = Accord.Math.Matrix.Identity(3);
            else MatA = this.GetAllIteration.inverse();
            double lamdaM=iter*0.01;
            MatA = MatA.multiplyMatrixByNumber(lamdaM);// finite matrix A!!!!!!!!!!!
          //  Console.WriteLine("first sample 3");
            //D=X^TAX
            PixelMatrix = semple1.toMatrix();
             helpa = PixelMatrix.Transpose();
             helpb = MatA.MultiplyMatrix(helpa);
             NewD1 = PixelMatrix.MultiplyMatrix(helpb);
             //NewD1 = calculateD(PixelMatrix, helpb);
                //----------------------------------------------------------------------//creating secound semples

             Console.WriteLine("sec sample");
                double[][] semple2 = getSamp(0,1);
             
              for (int i = 0; i < this.iter; i++)
             {
                KMeans kmeans2 = new KMeans(current_c, Distance.SquareEuclidean);
                int[] idx2 = kmeans2.Compute(semple2, 0.05);  //tolerance value

                myKmeans mykmean2 = new myKmeans(current_c, idx2, semple2,0.01);
                double[,] a = mykmean2.setAinIterationI();
                double[,] b = allIteration.addition(a);
                this.GetAllIteration = b;
             }
           //   Console.WriteLine("sec sample 2");
              if (this.GetAllIteration.Det() == 0.0)
                  MatA = Accord.Math.Matrix.Identity(3);
              else MatA = this.GetAllIteration.inverse();
              double lamdaM1 = iter * 0.01;
              MatA = MatA.multiplyMatrixByNumber(lamdaM1);// finite matrix A!!!!!!!!!!!
            //  Console.WriteLine("sec sample 3");
              //D=X^TAX
              PixelMatrix = semple2.toMatrix();
              helpc = PixelMatrix.Transpose();
              helpd = MatA.MultiplyMatrix(helpc);
              //NewD2 = calculateD(PixelMatrix, helpd);
              NewD2 = PixelMatrix.MultiplyMatrix(helpd);
                  //-----------------------------------------------------      
         
                Console.WriteLine("calc trace");
            double  result = calc_trace(NewD1, NewD2);
                //---------------------------------------------------------
          
              Console.WriteLine("after calc trace");
             this.resultJArr[j] = result;
        }//J for
            Array.Sort(this.resultJArr);

        }//get_resampl


        
    }
}
